#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Init.h>
#include <Kharlia/Engine/Actor.h>

namespace kharlia { namespace engine {

void _InitActorBindings() {
    enum_<EDirection::Type>("EDirection")
        .value("NORTH", EDirection::NORTH)
        .value("WEST", EDirection::WEST)
        .value("SOUTH", EDirection::SOUTH)
        .value("EAST", EDirection::EAST)
        //.export_values()
        ;

    enum_<ENetRole::Type>("ENetRole")
        .value("NONE", ENetRole::NONE)
        .value("SIM_PROXY", ENetRole::SIM_PROXY)
        .value("AUTO_PROXY", ENetRole::AUTO_PROXY)
        .value("AUTHORITY", ENetRole::AUTHORITY)
        //.export_values()
        ;

    // NEW STUFF

    class_<AComponent, bases<core::CObject>, noncopyable>
        ("AComponentBase", init<>())
        .add_property("Actor", &AComponent::GetActor)
        .add_property("_Actor", &AComponent::GetActor, &AComponent::SetActor)
        .add_property("Level", &AComponent::GetLevel)
        .add_property("_Level", &AComponent::GetLevel, &AComponent::SetLevel)
        .def_readonly("NoDelete", &AComponent::mNoDelete)
        .def_readwrite("_NoDelete", &AComponent::mNoDelete)
        .def_readonly("Priority", &AComponent::mPriority)
        .def_readwrite("_Priority", &AComponent::mPriority)
        .add_property("Inited", &AComponent::GetInited)
        .def_readwrite("NetDirty", &AComponent::mNetDirty)
        .def_readonly("NetInitial", &AComponent::mNetInitial)
        .def_readonly("NetOwner", &AComponent::mNetOwner)
        .def_readwrite("NetTestRelevancy", &AComponent::mNetTestRelevancy)
        .def(self < self)
        .def("Init", &AComponent::InitImpl)
        .def("Exit", &AComponent::ExitImpl)
        ;

    RegisterPtrConverter<AComponent>();
    KH_IMPL_GC(AComponent);

    class_<ATransform, bases<AComponent>, noncopyable>
        ("ATransformBase", init<>())
        .add_property("Position", make_getter(&ATransform::mPosition), &ATransform::SetPosition)
        .def("AddPosition", &ATransform::AddPosition)
        .def("Move", &ATransform::Move, (arg("delta"), arg("smooth")=true))
        .enable_pickling()
        ;

    RegisterPtrConverter<ATransform>();
    KH_IMPL_GC(ATransform);

    class_<AMovement, bases<AComponent>, noncopyable>
        ("AMovementBase", init<>())
        .def("Init", &AMovement::InitImpl)
        .def("Exit", &AMovement::ExitImpl)
        .add_property("Velocity", make_getter(&AMovement::mVelocity), &AMovement::SetVelocity)
        .add_property("Acceleration", make_getter(&AMovement::mAcceleration), &AMovement::SetAcceleration)
        .add_property("MaxVelocity", make_getter(&AMovement::mMaxVelocity), &AMovement::SetMaxVelocity)
        .add_property("MaxAcceleration", make_getter(&AMovement::mMaxAcceleration), &AMovement::SetMaxAcceleration)
        .enable_pickling()
        ;

    RegisterPtrConverter<AMovement>();
    KH_IMPL_GC(AMovement);

    class_<ARender, bases<AComponent>, noncopyable>
        ("ARenderBase", init<>())
        .def("Init", &ARender::InitImpl)
        .def("Exit", &ARender::ExitImpl)
        .add_property("Hidden", make_getter(&ARender::mHidden), &ARender::SetHidden)
        .enable_pickling()
        ;

    RegisterPtrConverter<ARender>();
    KH_IMPL_GC(ARender);

    class_<ASprite, bases<ARender>, noncopyable>
        ("ASpriteBase", init<>())
        .def("Init", &ASprite::InitImpl)
        .def("Exit", &ASprite::ExitImpl)
        .add_property("Image", make_function(&ASprite::GetImage, return_value_policy<return_by_value>()), &ASprite::SetImage)
        .add_property("Direction", &ASprite::GetDirection, &ASprite::SetDirection)
        .enable_pickling()
        ;

    class_<FSpriteFrame>
        ("FSpriteFrame", init<>())
        .def_readwrite("Rect", &FSpriteFrame::Rect)
        .def_readwrite("Time", &FSpriteFrame::Time)
        ;

    class_<ASpriteAnimation, bases<ASprite>, noncopyable>
        ("ASpriteAnimationBase", init<>())
        .def("Init", &ASpriteAnimation::InitImpl)
        .def("Exit", &ASpriteAnimation::ExitImpl)
        .def("Tick", &ASpriteAnimation::Tick)
        .def("Draw", &ASpriteAnimation::Draw)
        .def("GetDirectionFrameList", &ASpriteAnimation::GetDirectionFrameList)
        .add_property("CurrentFrameList", &ASpriteAnimation::GetCurrentFrameList, &ASpriteAnimation::SetCurrentFrameList)
        .add_property("CurrentFrame", &ASpriteAnimation::GetCurrentFrame, &ASpriteAnimation::SetCurrentFrame)
        .add_property("Definition", make_function(&ASpriteAnimation::GetDefinition, return_value_policy<return_by_value>()), &ASpriteAnimation::SetDefinition)
        .add_property("ShouldMapToDirections", &ASpriteAnimation::GetShouldMapToDirections, &ASpriteAnimation::SetShouldMapToDirections)
        //.def("SetFrame", &ASpriteAnimation::SetFrame)
        //.def("AddFrame", &ASpriteAnimation::AddFrame)
        //.def("GetFrame", (FSpriteFrame& (ASpriteAnimation::*)(uint16))&ASpriteAnimation::GetFrame,
        //                 return_internal_reference<>())
        //.add_property("FrameCount", &ASpriteAnimation::GetFrameCount)
        //.add_property("ShouldLoop", &ASpriteAnimation::GetShouldLoop, &ASpriteAnimation::SetShouldLoop)
        ;

    RegisterPtrConverter<ASprite>();
    KH_IMPL_GC(ASprite);

    class_<ACollider, bases<AComponent>, noncopyable>
        ("AColliderBase", init<>())
        .def("Init", &ACollider::InitImpl)
        .def("Exit", &ACollider::ExitImpl)
        .add_property("Bounds", make_getter(&ACollider::mBounds), &ACollider::SetBounds)
        .add_property("CollideActors", &ACollider::GetCollideActors, &ACollider::SetCollideActors)
        .add_property("CollideTiles", &ACollider::GetCollideTiles, &ACollider::SetCollideTiles)
        .add_property("BlockActors", &ACollider::GetBlockActors, &ACollider::SetBlockActors)
        .add_property("CollisionLayer", &ACollider::GetCollisionLayer, &ACollider::SetCollisionLayer)
        .def("BeginTouch", &ACollider::BeginTouchImpl)
        .def("EndTouch", &ACollider::EndTouchImpl)
        .def("Bump", &ACollider::BumpImpl)
        .def("EndTouches", &ACollider::EndTouches)
        .def("IsTouching", &ACollider::IsTouching)
        .add_property("ToucherCount", &ACollider::GetToucherCount)
        .def("GetToucher", &ACollider::GetToucher)
        .def("_AddToucher", &ACollider::AddToucher)
        .enable_pickling()
        ;
    
    RegisterPtrConverter<ACollider>();
    KH_IMPL_GC(ACollider);

    class_<ACamera, bases<AComponent>, noncopyable>
        ("ACameraBase", init<>())
        .def("Init", &ACamera::InitImpl)
        .def("Exit", &ACamera::ExitImpl)
        .add_property("Target", &ACamera::GetTarget, &ACamera::SetTarget)
        .add_property("TargetOffset", make_getter(&ACamera::mTargetOffset), &ACamera::SetTargetOffset)
        .enable_pickling()
        ;

    KH_IMPL_GC(ACamera);
    RegisterPtrConverter<ACamera>();
    
    class_<AController, bases<AComponent>, noncopyable>
        ("AControllerBase", init<>())
        .def("Init", &AController::InitImpl)
        .def("Exit", &AController::ExitImpl)
        .add_property("Pawn", &AController::GetPawn)
        .add_property("_Pawn", &AController::GetPawn, &AController::SetPawn)
        .def("Possess", pure_virtual(&AController::PossessImpl))
        .def("Unpossess", pure_virtual(&AController::UnpossessImpl))
        .enable_pickling()
        ;

    KH_IMPL_GC(AController);
    RegisterPtrConverter<AController>();
    
    class_<APlayerController, bases<AController>, noncopyable>
        ("APlayerControllerBase", init<>())
        .def("Init", &APlayerController::InitImpl)
        .def("Exit", &APlayerController::ExitImpl)
        .def("Tick", &APlayerController::TickImpl)
        .add_property("Player", &APlayerController::GetPlayer, &APlayerController::SetPlayer)
        .add_property("Camera", &APlayerController::GetCamera, &APlayerController::SetCamera)
        .enable_pickling()
        ;

    KH_IMPL_GC(APlayerController);
    RegisterPtrConverter<APlayerController>();

    class_<APawn, bases<AComponent>, noncopyable>
        ("APawnBase", init<>())
        .def("Init", &APawn::InitImpl)
        .def("Exit", &APawn::ExitImpl)
        .add_property("Controller", &APawn::GetController)
        .add_property("_Controller", &APawn::GetController, &APawn::SetController)
        //.def("PossessedBy", pure_virtual(&APawn::_Possessed))
        //.def("Unpossessed", pure_virtual(&APawn::_Unpossessed))
        .enable_pickling()
        ;

    KH_IMPL_GC(APawn);
    RegisterPtrConverter<APawn>();

    class_<ANetwork, bases<AComponent>, noncopyable>
        ("ANetworkBase", init<>())
        .def("Init", &ANetwork::InitImpl)
        .def("Exit", &ANetwork::ExitImpl)
        .def_readwrite("UseNativeRep", &ANetwork::mUseNativeRep)
        .def_readwrite("UsePythonRep", &ANetwork::mUsePythonRep)
        .def_readonly("HasNativeRep", &ANetwork::mHasNativeRep)
        .def_readonly("HasPythonRep", &ANetwork::mHasPythonRep)
        .def_readwrite("_HasPythonRep", &ANetwork::mHasPythonRep)
        .add_property("NetworkID", &ANetwork::GetNetworkID)
        .enable_pickling()
        ;

    RegisterPtrConverter<ANetwork>();

    SetModuleAttr("UNASSIGNED_NETWORK_ID", object(rn::UNASSIGNED_NETWORK_ID));

#define EXP_SLOT(TYPE) .add_property(#TYPE, make_function(&CActor::Get##TYPE, return_value_policy<copy_const_reference>()))

    class_<CActor, bases<core::CObject>, noncopyable>
        ("CActorBase", init<>())
        .def_readonly("ID", &CActor::mID)
        .def_readwrite("_ID", &CActor::mID)
        .def_readwrite("Tag", &CActor::mTag)
        .def_readonly("Name", &CActor::mName)
        .add_property("Level", &CActor::GetLevel)
        .add_property("_Level", &CActor::GetLevel, &CActor::SetLevel)
        .add_property("Owner", &CActor::GetOwner, &CActor::SetOwner)
        .add_property("NoDelete", &CActor::GetNoDelete)
        .add_property("ComponentCount", &CActor::GetComponentCount)
        .add_property("ChildCount", &CActor::GetChildCount)
        .add_property("Role", &CActor::GetRole, &CActor::SetRole)
        .add_property("RemoteRole", &CActor::GetRemoteRole, &CActor::SetRemoteRole)
        .add_property("AlwaysRelevant", &CActor::GetAlwaysRelevant, &CActor::SetAlwaysRelevant)
        .add_property("OnlyRelevantToOwner", &CActor::GetOnlyRelevantToOwner, &CActor::SetOnlyRelevantToOwner)
        .def_readonly("DeleteMe", &CActor::mDeleteMe)
        EXP_SLOT(Transform)
        EXP_SLOT(Movement)
        EXP_SLOT(Render)
        EXP_SLOT(Collider)
        EXP_SLOT(Camera)
        EXP_SLOT(Pawn)
        EXP_SLOT(PlayerController)
        EXP_SLOT(Network)
        .def("Init", &CActor::Init)
        .def("Exit", &CActor::Exit)
        .def("Rename", &CActor::Rename)
        .def("GetComponent", (TPtr<AComponent> (CActor::*)(object) const)&CActor::GetComponent)
        .def("AddComponent", &CActor::AddComponent)
        .def("RemoveComponent", &CActor::RemoveComponent)
        .def("GetChild", &CActor::GetChild)
        .def("__len__", &CActor::GetComponentCount)
        .def("__getitem__", (TPtr<AComponent> (CActor::*)(uint32) const)&CActor::GetComponent)
        .def_pickle(CActor::Pickler())
        ;

    KH_IMPL_GC(CActor);
    RegisterPtrConverter<CActor>();
}

}}